
#ifndef __WIN32__
#include <termios.h>
#endif
#include "../utils/StringUtils.h"
#include "../utils/BaseConvert.h"
#include "KeyDB.h"
#include "../crypt/RSAKeyPair.h"
#include "../crypt/RSAPublicKey.h"
#include "../crypt/RSAPrivateKey.h"
#include <apr_date.h>
#include <apr_strings.h>
#include <apr_file_io.h>
#include "../iface/APRInterface.h"

#define MAX_PASSWORD_LENGTH 64

int KeyDB::AskIsThisOkay(const char *prompt)
{
   char *result = NULL;
   while (1)
   {
      int retval = -1;
      result = AskPrompt(prompt);
      if (result == NULL) retval = 0; // ASSUMING THAT NO IS THE SAFE OPTION!
      if (strcmp(result, "Yes") == 0) retval = 1;
      if (strcmp(result, "YES") == 0) retval = 1;
      if (strcmp(result, "yes") == 0) retval = 1;
      if (strcmp(result, "No") == 0) retval = 0;
      if (strcmp(result, "NO") == 0) retval = 0;
      if (strcmp(result, "no") == 0) retval = 0;
      if (retval != -1)
      {
         if (result) free(result);
         return retval;
      }
      printf("Please type 'yes' or 'no'.\n");
   }
}

char *KeyDB::AskPassword(const char *prompt)
{
#ifdef PASSDEFINE
   char *password = (char *)malloc(sizeof(char)*(MAX_PASSWORD_LENGTH+1+1));
   sprintf(password, "hello");
   return password;
#else
   return AskPasswordEx(prompt);
#endif
}

char *KeyDB::AskPasswordEx(const char *prompt)
{
   // This function should never be used in WIN32 anyway
#ifdef __WIN32__
   return AskPrompt(prompt);
#else
   char *password = (char *)malloc(sizeof(char)*(MAX_PASSWORD_LENGTH+1+1));
   struct termios con_old, con_new;
   printf("%s", prompt);
   if (tcgetattr(fileno(stdin), &con_old) != 0)
      return NULL;
   con_new = con_old;
   con_new.c_lflag &= ~ECHO;
   if (tcsetattr(fileno(stdin), TCSAFLUSH, &con_new) != 0)
      return NULL;
   char *p;
   if ((p = fgets(password, MAX_PASSWORD_LENGTH+1, stdin)) != NULL)
   {
      size_t finalc = strlen(password);
      // Remove trailing control characters
      // (non-printable - reasonable assumption that it's whitespace)
      while (password[--finalc] < ((char)0x20))
         password[finalc] = '\0';
   } else
   {
      free(password);
      password = NULL;
   }
   tcsetattr(fileno(stdin), TCSAFLUSH, &con_old);
   printf("\n");
   return password;
#endif
}

char *KeyDB::AskPrompt(const char *prompt)
{
   char *password = (char *)malloc(sizeof(char)*(MAX_PASSWORD_LENGTH+1+1));
   char *p;

   printf("%s", prompt);
#ifdef PASSDEFINE
   sprintf(password, "yes");
#else
   if ((p = fgets(password, MAX_PASSWORD_LENGTH+1, stdin)) != NULL)
   {
      size_t finalc = strlen(password);
      // Remove trailing control characters
      // (non-printable - reasonable assumption that it's whitespace)
      while (password[--finalc] < ((char)0x20))
         password[finalc] = '\0';
   } else
   {
      free(password);
      password = NULL;
   }
#endif
   return password;
}

void KeyDB::CreateTablesIfNecessary()
{
   EnsureTablesAreCreated(2,
      "private_key", "(keyname char(64) primary key not null, keytype char(32), keydata char(4096), owner char(64), latest char(1), usedrefcount decimal(10,0), existsrefcount decimal(10, 0), rfc822date char(32))",
      "public_key", "(keyname char(64) primary key not null, keytype char(32), keydata char(4096), owner char(64), latest char(1), usedrefcount decimal(10,0), existsrefcount decimal(10, 0), rfc822date char(32))");
}

std::vector<char *> *KeyDB::GetPrivateKeyStringList(const char *named)
{
   std::vector<char *> *prv_strlist =
      SQLScanRowTextf("select keyname,keytype,keydata from 'private_key' where keyname = '%s';", named);
   return prv_strlist;
}

std::vector<char *> *KeyDB::GetPublicKeyStringList(const char *named)
{
   std::vector<char *> *pub_strlist =
      SQLScanRowTextf("select keyname,keytype,keydata from 'public_key' where keyname = '%s';", named);
   return pub_strlist;
}

#define OBTAIN_NAMED_KEYPAIR__WRONG_PASSWORD   -1
#define OBTAIN_NAMED_KEYPAIR__NO_ERROR         0
#define OBTAIN_NAMED_KEYPAIR__NO_EXISTING_KEYS 1
#define OBTAIN_NAMED_KEYPAIR__BROKEN_KEYS      2

KeyPair *KeyDB::ObtainNamedKeyPair(const char *named, const char *passprompt, char **password, int *errorcode)
{
   std::vector<char *> *prv_strlist = GetPrivateKeyStringList(named);
   std::vector<char *> *pub_strlist = GetPublicKeyStringList(named);
   int key_okay = 0;
   KeyPair *keypair = NULL;
   if ((prv_strlist) && (pub_strlist))
   {
      if ((strcmp(prv_strlist->at(1), "RSA") == 0) && (strcmp(pub_strlist->at(1), "RSA") == 0))
      {
         RSAKeyPair *rkeypair = new RSAKeyPair();
         if ((passprompt) && (*password == NULL)) *password = AskPassword(passprompt);
         int result;
         if (*password)
         {
            if (strlen(*password) > 0)
            {
               result = rkeypair->ImportPasswordedPrivateKey(*password, prv_strlist->at(2));
            } else
            {
               result = rkeypair->ImportPrivateKey(prv_strlist->at(2));
            }
         }
         else
            result = rkeypair->ImportPrivateKey(prv_strlist->at(2));
         if (result == 0)
         {
            rkeypair->ImportPublicKey(pub_strlist->at(2));
            BN_free(rkeypair->prv->n);
            rkeypair->prv->n = BN_new();
            if (BN_copy(rkeypair->prv->n, rkeypair->pub->n) != (rkeypair->prv->n))
               errprint("public to private key (n) copy for key check failed");
            BN_free(rkeypair->prv->e);
            rkeypair->prv->e = BN_new();
            if (BN_copy(rkeypair->prv->e, rkeypair->pub->e) != (rkeypair->prv->e))
               errprint("public to private key (e) copy for key check failed");
            key_okay = RSA_check_key(rkeypair->prv);
            if (!key_okay)
               errprint("Key import failed (keys failed sanity checks)");
            if (key_okay < 0)
               errprint("Errors occurred while checking keys (keys failed sanity checks)");
            if ((errorcode) && (key_okay == 1))
               *errorcode = OBTAIN_NAMED_KEYPAIR__NO_ERROR;
            else
               *errorcode = OBTAIN_NAMED_KEYPAIR__BROKEN_KEYS;
         } else
         {
            errprint("Key import failed (bad password?)");
            key_okay = 0;
            if (errorcode) *errorcode = OBTAIN_NAMED_KEYPAIR__WRONG_PASSWORD;
         }
         if (!key_okay)
         {
            delete rkeypair;
            rkeypair = NULL;
         } else
         {
            keypair = rkeypair;
            printf("Local RSA keypair loaded\n");
         }
      } else
      {
         if (errorcode) *errorcode = OBTAIN_NAMED_KEYPAIR__BROKEN_KEYS;
         errprint("Unrecognised key type.");
      }
   } else
   {
      if (errorcode) *errorcode = OBTAIN_NAMED_KEYPAIR__NO_EXISTING_KEYS;
      errprint("Could not find keys.");
   }
   if (prv_strlist) StringUtils::DestroyStringList(prv_strlist);
   if (pub_strlist) StringUtils::DestroyStringList(pub_strlist);
   return keypair;
}

KeyPair *KeyDB::ObtainNewNamedKeyPair(const char *named, const char *passprompt, char **password, int nbits)
{
   RSAKeyPair *rkeypair = new RSAKeyPair();
   errprint("generating new RSA key pair for '%s'", named);
   rkeypair->GenerateKeyPair(nbits);
   if ((passprompt) && (*password == NULL)) *password = AskPassword(passprompt);
   AddPrivateKeyWithPassword(named, *password, (KeyPair *)rkeypair, NULL);
   AddPublicKey(named, (KeyPair *)rkeypair, NULL);
   MarkKeyPairAsLatest(named);
   //DumpDatabaseToSQL(dumpfilename);
   return (KeyPair *)rkeypair;
}

void KeyDB::GenerateKeyPairIntoDB(const char *owner, const char *name, const char *password, int nbits)
{
   RSAKeyPair *rkeypair = new RSAKeyPair();
   errprint("generating new RSA key pair for '%s'", name);
   rkeypair->GenerateKeyPair(nbits);
   AddPrivateKeyWithPassword(name, password, (KeyPair *)rkeypair, owner);
   AddPublicKey(name, (KeyPair *)rkeypair, owner);
   delete rkeypair;
}

void KeyDB::AddPrivateKey(const char *name, KeyPair *keypair, const char *owner)
{
   // Massive change here! - oops
   errprint("serious warning - appears as though '%s' is unencrypted!", name);
   AddPrivateKeyWithPassword(name, NULL, keypair, owner);
}

void KeyDB::AddPrivateKeyWithPassword(const char *name, const char *password, KeyPair *keypair, const char *owner)
{
   char timebuf[APR_RFC822_DATE_LEN + 1];
   apr_status_t status;
   apr_time_t timet = apr_time_now();
   status = apr_rfc822_date(timebuf, timet);
   CheckAPRError(status);
   char *priv_export;
   if (password)
   { 
      if (strlen(password) > 0)
      {
         priv_export = keypair->ExportPasswordedPrivateKey(password);
      } else
      {
         priv_export = keypair->ExportPrivateKey();
      }
   }
   else
      priv_export = keypair->ExportPrivateKey();
   char *previous_keyname = NULL;
   sqlite3_mutex_enter(dbmutex);
   // Find the previously latest key for the particular owner
   std::vector<char *> *prv_strlist =
      SQLScanRowTextf_unsafe("select keyname from 'private_key' where owner='%s' and latest='Y';", (owner) ? owner : name);
   if ((prv_strlist) && (prv_strlist->size() > 0))
   {
      previous_keyname = (char *)malloc(sizeof(char)*(strlen(prv_strlist->at(0))+1));
      strcpy(previous_keyname, prv_strlist->at(0));
      StringUtils::DestroyStringList(prv_strlist);
   } else if (prv_strlist) delete prv_strlist;
   SQLInstantExecf_unsafe("insert or replace into private_key (keyname, keytype, keydata, owner, latest, usedrefcount, existsrefcount, rfc822date) values ('%s', '%s', '%s', '%s', '%c', '%d', '%d', '%s');",
      name, keypair->GetKeyTypeName(), priv_export, (owner) ? owner : name, 'Y', 0, 0, timebuf);
   if (previous_keyname)
   {
      SQLInstantExecf_unsafe("update 'private_key' set latest='N' where keyname='%s';", previous_keyname);
      free(previous_keyname);
   }
   sqlite3_mutex_leave(dbmutex);
   if (priv_export) free(priv_export);
}

void KeyDB::AddPublicKey(const char *name, KeyPair *keypair, const char *owner)
{
   char *exportedkey = keypair->ExportPublicKey();
   AddPublicKey(name, keypair->GetKeyTypeName(), exportedkey, owner);
   free(exportedkey);
}

void KeyDB::AddPublicKey(const char *name, const char *keytype, const char *keydata, const char *owner)
{
   if (keydata == NULL) { errprint("Key data is NULL"); return; }
   if (strlen(keydata) == 0) { errprint("Key data is empty"); return; }
   char timebuf[APR_RFC822_DATE_LEN + 1];
   apr_status_t status;
   apr_time_t timet = apr_time_now();
   status = apr_rfc822_date(timebuf, timet);
   CheckAPRError(status);
   char *previous_keyname = NULL;
   sqlite3_mutex_enter(dbmutex);
   // Find the previously latest key for the particular owner
   std::vector<char *> *pub_strlist =
      SQLScanRowTextf_unsafe("select keyname from 'public_key' where owner='%s' and latest='Y';", (owner) ? owner : name);
   if ((pub_strlist) && (pub_strlist->size() > 0))
   {
      previous_keyname = (char *)malloc(sizeof(char)*(strlen(pub_strlist->at(0))+1));
      strcpy(previous_keyname, pub_strlist->at(0));
      StringUtils::DestroyStringList(pub_strlist);
   } else if (pub_strlist) delete pub_strlist;
   SQLInstantExecf_unsafe("insert or replace into public_key (keyname, keytype, keydata, owner, latest, usedrefcount, existsrefcount, rfc822date) values ('%s', '%s', '%s', '%s', '%c', '%d', '%d', '%s');",
      name, keytype, keydata, (owner) ? owner : name, 'Y', 0, 0, timebuf);
   if (previous_keyname)
   {
      SQLInstantExecf_unsafe("update 'public_key' set latest='N' where keyname='%s';", previous_keyname);
      free(previous_keyname);
   }
   sqlite3_mutex_leave(dbmutex);
}

int KeyDB::GetMainKeyset(KeyPair **kp, const char *name, int nobits, char *password)
{
   char *tpasswordcpy = NULL;
   if (password)
   {
      tpasswordcpy = (char *)malloc(sizeof(char)*(strlen(password)+1));
      strcpy(tpasswordcpy, password);
   }
   int errorcode = 0;
   *kp = ObtainNamedKeyPair(name, "Please enter password to unlock the private key (or create a new key if one does not exist): ", &tpasswordcpy, &errorcode);
   if (!(*kp)) *kp = ObtainNewNamedKeyPair(name, NULL, &tpasswordcpy, nobits);
   if (tpasswordcpy)
   {
      if (WIPpassword == NULL)
      {
         WIPpassword = (char *)malloc(sizeof(char)*(strlen(tpasswordcpy)+1));
         strcpy(WIPpassword, tpasswordcpy);
      }
      memset(tpasswordcpy, 0, strlen(tpasswordcpy));
      free(tpasswordcpy);
      tpasswordcpy = NULL;
   }
   if (!(*kp)) { errprint("No keypair loaded."); return 0; }
   return 1;
}

KeyPair *KeyDB::GetPublicKeyByName(const char *name)
{
   KeyPair *kp = NULL;
   if (name == NULL) return NULL;
   if (strcmp(name, "NULL") == 0) return NULL;
   std::vector<char *> *pub_strlist = GetPublicKeyStringList(name);
   if (pub_strlist == NULL) return NULL;
   if (pub_strlist->size() == 0) { StringUtils::DestroyStringList(pub_strlist); return NULL; }
   if (strcmp(pub_strlist->at(1), "RSA") == 0)
   {
      RSAKeyPair *rkeypair = new RSAKeyPair();
      if (rkeypair->ImportPublicKey(pub_strlist->at(2)) == 0)
         kp = (KeyPair *)rkeypair;
      else
         delete rkeypair;
   }
   StringUtils::DestroyStringList(pub_strlist);
   return kp;
}

KeyPair *KeyDB::GetPrivateKeyByName(const char *name)
{
   return GetPrivateKeyByName(name, WIPpassword);
}

KeyPair *KeyDB::GetPrivateKeyByName(const char *name, const char *Apassword)
{
   KeyPair *kp = NULL;
   if (name == NULL) return NULL;
   if (strcmp(name, "NULL") == 0) return NULL;
   const char *aname = name;
   std::vector<char *> *prv_strlist = GetPrivateKeyStringList(aname);
   if ((prv_strlist == NULL) || (prv_strlist->size() == 0))
   {
      // Try again with temporary key (remote end doesn't trust us yet)
      if (prv_strlist) StringUtils::DestroyStringList(prv_strlist);
      prv_strlist = GetPrivateKeyStringList(&(aname[1]));
   }
   // Needs keyname, keytype and keydata - 3!
   if ((prv_strlist == NULL) || (prv_strlist->size() != 3))
   {
      if (prv_strlist) StringUtils::DestroyStringList(prv_strlist);
      errprint("Private key '%s' is not found, trying main key '%s'", aname, selfkey);
      aname = selfkey;
      prv_strlist = GetPrivateKeyStringList(aname);
   }
   if (strcmp(prv_strlist->at(1), "RSA") == 0)
   {
      RSAKeyPair *rkeypair = new RSAKeyPair();
      if ((selfkey != NULL) && (strcmp(aname, (const char *)selfkey) == 0))
      {
         if (!(RSAKeyPair::RSACopyPrivateKey(rkeypair->prv, ((RSAKeyPair *)selfkeypair)->prv)))
         {
            kp = (KeyPair *)rkeypair;
         } else kp = NULL;
      } else if (Apassword)
      {
         int result = 0;
         if (strlen(Apassword) > 0)
         {
            result = rkeypair->ImportPasswordedPrivateKey(Apassword, prv_strlist->at(2));
         } else
         {
            result = rkeypair->ImportPrivateKey(prv_strlist->at(2));
         }
         kp = (result == 0) ? (KeyPair *)rkeypair : NULL;
         if (!kp)
            errprint("Could not import private key '%s'", name);
      } else
      {
         int result = rkeypair->ImportPrivateKey(prv_strlist->at(2));
         kp = (result == 0) ? (KeyPair *)rkeypair : NULL;
         if (!kp)
            errprint("Could not import private key '%s' (no password)", name);
      }
      if (!kp) delete rkeypair;
   }
   StringUtils::DestroyStringList(prv_strlist);
   return kp;
}

KeyPair *KeyDB::GetLatestPublicKeyByOwner(const char *owner)
{
   char *latestkeyname = GetLatestPublicKeyNameOfOwner(owner);
   if (latestkeyname == NULL) return NULL;
   KeyPair *kp = GetPublicKeyByName(latestkeyname);
   free(latestkeyname);
   return kp;
}

KeyPair *KeyDB::GetLatestPrivateKeyByOwner(const char *owner)
{
   char *latestkeyname = GetLatestPrivateKeyNameOfOwner(owner);
   if (latestkeyname == NULL) return NULL;
   KeyPair *kp = GetPrivateKeyByName(latestkeyname);
   free(latestkeyname);
   return kp;
}

PublicKey *KeyDB::NewGetPublicKeyByName(const char *name)
{
   PublicKey *pubk = NULL;
   if (name == NULL) return NULL;
   if (strcmp(name, "NULL") == 0) return NULL;
   std::vector<char *> *pub_strlist = GetPublicKeyStringList(name);
   if (pub_strlist == NULL) return NULL;
   if (pub_strlist->size() == 0) { StringUtils::DestroyStringList(pub_strlist); return NULL; }
   if (strcmp(pub_strlist->at(1), "RSA") == 0)
   {
      RSAPublicKey *rpubkey = new RSAPublicKey(pub_strlist->at(2), name);
      pubk = (PublicKey *)rpubkey;
   } else
   {
      errprint("unrecoginised key type when getting public key '%s' from database", name);
   }
   StringUtils::DestroyStringList(pub_strlist);
   return pubk;
}

/*
PrivateKey *KeyDB::NewGetPrivateKeyByName(const char *name)
{
   return NewGetPrivateKeyByName(name, WIPpassword);
}
*/
/*
PrivateKey *KeyDB::NewGetPrivateKeyByName(const char *name, const char *Apassword)
{
   PrivateKey *prvk = NULL;
   if (name == NULL) return NULL;
   if (strcmp(name, "NULL") == 0) return NULL;
   const char *aname = name;
   std::vector<char *> *prv_strlist = GetPrivateKeyStringList(aname);
   if ((prv_strlist == NULL) || (prv_strlist->size() == 0))
   {
      // Try again with temporary key (remote end doesn't trust us yet)
      if (prv_strlist) StringUtils::DestroyStringList(prv_strlist);
      prv_strlist = GetPrivateKeyStringList(&(aname[1]));
   }
   // Needs keyname, keytype and keydata - 3!
   if ((prv_strlist == NULL) || (prv_strlist->size() != 3))
   {
      if (prv_strlist) StringUtils::DestroyStringList(prv_strlist);
      errprint("Private key '%s' is not found, trying main key '%s'", aname, selfkey);
      aname = selfkey;
      prv_strlist = GetPrivateKeyStringList(aname);
   }
   if (prv_strlist == NULL) return NULL;
   if (prv_strlist->size() == 0) { StringUtils::DestroyStringList(prv_strlist); return NULL; }
   if (strcmp(prv_strlist->at(1), "RSA") == 0)
   {
      RSAPrivateKey *rprvkey = NULL;
      if ((selfkey != NULL) && (strcmp(aname, (const char *)selfkey) == 0) && (strcmp(selfkeyprivate->GetKeyType(), "RSAPrivate") == 0))
      {
         rprvkey = new RSAPrivateKey(*selfkeyprivate);
      } else if (Apassword)
      {
         int result = 0;
         if (strlen(Apassword) > 0)
         {
            rprvkey = new RSAPrivateKey(prv_strlist->at(2), aname);
         } else
         {
            rprvkey = new RSAPrivateKey(prv_strlist->at(2), Apassword, aname);
         }
      } else
      {
         rprvkey = new RSAPrivateKey(prv_strlist->at(2), aname);
      }
      prvk = (PrivateKey *)rprvkey;
   } else
   {
      errprint("unrecoginised key type when getting private key '%s' from database", aname);
   }
   StringUtils::DestroyStringList(prv_strlist);
   return prvk;
}

PublicKey *KeyDB::NewGetLatestPublicKeyByOwner(const char *owner)
{
   
}

      PrivateKey *NewGetLatestPrivateKeyByOwner(const char *owner);
*/

void KeyDB::DeletePublicKeyByName(const char *name)
{
   SQLInstantExecf("delete from 'public_key' where keyname='%s';", name);
}

void KeyDB::DeletePrivateKeyByName(const char *name)
{
   SQLInstantExecf("delete from 'private_key' where keyname='%s';", name);
}

int KeyDB::OutputNamedPublicKeyToFile(const char *keyname, const char *filename, apr_pool_t *mp)
{
   if ((keyname == NULL) || (strlen(keyname) == 0))
   {
      errprint("cannot use %s keyname", (keyname) ? "an empty" : "a NULL");
      return -1;
   }
   KeyPair *this_kp = GetPublicKeyByName(keyname);
   if (this_kp == NULL)
   {
      errprint("this key '%s' does not exist", keyname);
      return -1;
   }
   char *exportedkey = this_kp->ExportPublicKey();
   delete this_kp;
   int retval = StringUtils::WriteStringToFile(exportedkey, filename, mp);
   free(exportedkey);
   return retval;
}

int KeyDB::InputPublicKeyFromFile(const char *filename, const char *keyname, const char *owner, apr_pool_t *mp)
{
   apr_file_t *newFP;
   apr_status_t status;
   status = apr_file_open(&newFP, StringUtils::expandMyHomeDirectoryTilde(filename, mp), APR_FOPEN_BINARY|APR_FOPEN_READ|APR_FOPEN_BUFFERED, APR_FPROT_OS_DEFAULT, mp);
   CheckAPRError(status);
   if (status != APR_SUCCESS)
   {
      errprint("File could not be opened to read key.");
      return -1;
   }
   apr_finfo_t finfo;
#ifdef USE_STAT_FILE_SIZE
   status = apr_file_info_get(&finfo, APR_FINFO_SIZE, newFP);
   CheckAPRError(status);
#else
   apr_off_t offset = 0;
   status = apr_file_seek(newFP, APR_END, &offset);
   CheckAPRError(status);
   finfo.size = offset;
   offset = 0;
   status = apr_file_seek(newFP, APR_SET, &offset);
   CheckAPRError(status);
#endif
   apr_size_t fullread = finfo.size;
   char *pkey = (char *)malloc(sizeof(char)*(finfo.size+1));
   status = apr_file_read_full(newFP, pkey, finfo.size, &fullread);
   CheckAPRError(status);
   pkey[fullread] = '\0';
   if (fullread != finfo.size)
   {
      errprint("getting public key '%s' from file - expected to read %d bytes, actually read %d", keyname, finfo.size, fullread);
      status = apr_file_close(newFP);
      CheckAPRError(status);
      free(pkey);
      return -1;
   }
   status = apr_file_close(newFP);
   CheckAPRError(status);
   AddPublicKey(keyname, "RSA", pkey, owner);
   free(pkey);
   return 0;
}

int KeyDB::InputPrivateKeyFromFile(const char *filename, const char *keyname, const char *owner, apr_pool_t *mp)
{
   apr_file_t *newFP;
   apr_status_t status;
   status = apr_file_open(&newFP, StringUtils::expandMyHomeDirectoryTilde(filename, mp), APR_FOPEN_BINARY|APR_FOPEN_READ|APR_FOPEN_BUFFERED, APR_FPROT_OS_DEFAULT, mp);
   CheckAPRError(status);
   if (status != APR_SUCCESS)
   {
      errprint("File could not be opened to read key.");
      return -1;
   }
   apr_finfo_t finfo;
#ifdef USE_STAT_FILE_SIZE
   status = apr_file_info_get(&finfo, APR_FINFO_SIZE, newFP);
   CheckAPRError(status);
#else
   apr_off_t offset = 0;
   status = apr_file_seek(newFP, APR_END, &offset);
   CheckAPRError(status);
   finfo.size = offset;
   offset = 0;
   status = apr_file_seek(newFP, APR_SET, &offset);
   CheckAPRError(status);
#endif
   apr_size_t fullread = finfo.size;
   char *pkey = (char *)malloc(sizeof(char)*(finfo.size+1));
   status = apr_file_read_full(newFP, pkey, finfo.size, &fullread);
   CheckAPRError(status);
   pkey[fullread] = '\0';
   if (fullread != finfo.size)
   {
      errprint("getting private key '%s' from file - expected to read %d bytes, actually read %d", keyname, finfo.size, fullread);
      status = apr_file_close(newFP);
      CheckAPRError(status);
      free(pkey);
      return -1;
   }
   status = apr_file_close(newFP);
   CheckAPRError(status);
   RSAKeyPair *rsakp = new RSAKeyPair();
   rsakp->ImportPrivateKey(pkey);
   AddPrivateKey(keyname, (KeyPair *)rsakp, owner);
   delete rsakp;
   free(pkey);
   return 0;
}

char *KeyDB::GetLatestPublicKeyNameOfOwner(const char *owner)
{
   std::vector<char *> *pub_strlist = SQLScanRowTextf("select keyname from 'public_key' where owner='%s' and (latest='Y' or latest='E' or latest='X');", owner);
   char *resultant = NULL;
   if ((pub_strlist) && (pub_strlist->size() > 0))
   {
      resultant = (char *)malloc(sizeof(char)*(strlen(pub_strlist->at(0))+1));
      strcpy(resultant, pub_strlist->at(0));
      StringUtils::DestroyStringList(pub_strlist);
   } else
   {
      errprint("could not find latest public key owned by '%s'", owner);
      if (pub_strlist) delete pub_strlist;
   }
   return resultant;
}

char *KeyDB::GetLatestPrivateKeyNameOfOwner(const char *owner)
{
   std::vector<char *> *prv_strlist = SQLScanRowTextf("select keyname from 'private_key' where owner='%s' and (latest='Y' or latest='E' or latest='X');", owner);
   char *resultant = NULL;
   if ((prv_strlist) && (prv_strlist->size() > 0))
   {
      resultant = (char *)malloc(sizeof(char)*(strlen(prv_strlist->at(0))+1));
      strcpy(resultant, prv_strlist->at(0));
      StringUtils::DestroyStringList(prv_strlist);
   } else
   {
      errprint("could not find latest private key owned by '%s'", owner);
      if (prv_strlist) delete prv_strlist;
   }
   return resultant;
}

int KeyDB::TryToUpdateOwnersKeys(KeyDB *otherkeydb, const char *owner, int keylifetime_secs)
{
   // This only makes sense if the particular physical part owns the keypair
   std::vector<char *> *prv_strlist = SQLScanRowTextf("select keyname,rfc822date from 'private_key' where owner='%s' and latest='Y';", owner);
   if ((prv_strlist) && (prv_strlist->size() > 0))
   {
      apr_time_t key_creation_time = apr_date_parse_rfc(prv_strlist->at(1));
      apr_time_t this_time_now = apr_time_now();
      if ((this_time_now - key_creation_time) > (1000000 * keylifetime_secs))
      {
         // time to create a new latest key
         RSAKeyPair *rkp = new RSAKeyPair();
         rkp->GenerateKeyPair(KEY_BITS);
         char *newuuid;
         // We don't worry so much about hash collisions on the client - we expect the client to have more entropy!
         if (otherkeydb)
            newuuid = BaseConvert::MakeHexUUID_PlusUniq2(24, this, otherkeydb);
         else
            newuuid = BaseConvert::MakeHexUUID_PlusUniq_deprecated(24, this);
         AddPrivateKeyWithPassword(newuuid, WIPpassword, (KeyPair *)rkp, owner);
         AddPublicKey(newuuid, (KeyPair *)rkp, owner);
         delete rkp;
         free(newuuid);
         // Old key will become deprecated automatically
         StringUtils::DestroyStringList(prv_strlist);
         return 1;
      }
      StringUtils::DestroyStringList(prv_strlist);
   } else
   {
      errprint("could not find a key pair owned by '%s'", owner);
      if (prv_strlist) delete prv_strlist;
   }
   return 0;
}

void KeyDB::RollbackOwnersKeyToPreviousKey(const char *owner, const char *oldkeyname)
{
   // Need to remember to change refcounts accordingly!
   char *newkeyname = GetLatestPublicKeyNameOfOwner(owner);
   DeletePrivateKeyByName(newkeyname);
   DeletePublicKeyByName(newkeyname);
   free(newkeyname);
   SQLInstantExecf("update 'public_key' set latest='Y' where keyname='%s';", oldkeyname);
   SQLInstantExecf("update 'private_key' set latest='Y' where keyname='%s';", oldkeyname);
}

void KeyDB::KeyPairNowUsedByActor(const char *keyuuid)
{
   sqlite3_mutex_enter(dbmutex);
   SQLInstantExecf_unsafe("update 'public_key' set usedrefcount=usedrefcount+1 where keyname='%s';", keyuuid);
   SQLInstantExecf_unsafe("update 'public_key' set existsrefcount=existsrefcount+1 where keyname='%s';", keyuuid);
   SQLInstantExecf_unsafe("update 'public_key' set latest='N' where keyname='%s' and latest='D';", keyuuid);
   SQLInstantExecf_unsafe("update 'public_key' set latest='Y' where keyname='%s' and (latest='E' or latest='X');", keyuuid);
   SQLInstantExecf_unsafe("update 'private_key' set usedrefcount=usedrefcount+1 where keyname='%s';", keyuuid);
   SQLInstantExecf_unsafe("update 'private_key' set existsrefcount=existsrefcount+1 where keyname='%s';", keyuuid);
   SQLInstantExecf_unsafe("update 'private_key' set latest='N' where keyname='%s' and latest='D';", keyuuid);
   SQLInstantExecf_unsafe("update 'private_key' set latest='Y' where keyname='%s' and (latest='E' or latest='X');", keyuuid);
   sqlite3_mutex_leave(dbmutex);
}

void KeyDB::KeyNowUnusedByActor(const char *keyuuid)
{
   sqlite3_mutex_enter(dbmutex);
   SQLInstantExecf_unsafe("update 'public_key' set usedrefcount=usedrefcount-1 where keyname='%s';", keyuuid);
   SQLInstantExecf_unsafe("update 'private_key' set usedrefcount=usedrefcount-1 where keyname='%s';", keyuuid);
   sqlite3_mutex_leave(dbmutex);
}

void KeyDB::KeyNowDeletedByActor(const char *keyuuid)
{
   sqlite3_mutex_enter(dbmutex);
   SQLInstantExecf_unsafe("update 'public_key' set existsrefcount=existsrefcount-1 where keyname='%s';", keyuuid);
   SQLInstantExecf_unsafe("update 'private_key' set existsrefcount=existsrefcount-1 where keyname='%s';", keyuuid);
   sqlite3_mutex_leave(dbmutex);
}

int KeyDB::EnsureChainBaseAnchored(const char *keyuuid)
{
   int upub = 1, uprv = 1, epub = 1, eprv = 1;
   int zeroes = 0;
   if (SQLGetSingleIntf(&upub, "select usedrefcount from 'public_key' where keyname='%s';", keyuuid))
      if (upub == 0) zeroes++;
   if (SQLGetSingleIntf(&uprv, "select usedrefcount from 'private_key' where keyname='%s';", keyuuid))
      if (uprv == 0) zeroes++;
   if (SQLGetSingleIntf(&epub, "select existsrefcount from 'public_key' where keyname='%s';", keyuuid))
      if (epub == 0) zeroes++;
   if (SQLGetSingleIntf(&eprv, "select existsrefcount from 'private_key' where keyname='%s';", keyuuid))
      if (eprv == 0) zeroes++;
   if (zeroes) KeyPairNowUsedByActor(keyuuid);
   return zeroes;
}

int KeyDB::GetPublicKeyUsedCount(const char *keyuuid)
{
   int times = 0;
   SQLGetSingleIntf(&times, "select usedrefcount from 'public_key' where keyname='%s';", keyuuid);
   return times;
}

int KeyDB::GetPrivateKeyUsedCount(const char *keyuuid)
{
   int times = 0;
   SQLGetSingleIntf(&times, "select usedrefcount from 'private_key' where keyname='%s';", keyuuid);
   return times;
}

int KeyDB::GetPublicKeyExistsCount(const char *keyuuid)
{
   int times = 0;
   SQLGetSingleIntf(&times, "select existsrefcount from 'public_key' where keyname='%s';", keyuuid);
   return times;
}

int KeyDB::GetPrivateKeyExistsCount(const char *keyuuid)
{
   int times = 0;
   SQLGetSingleIntf(&times, "select existsrefcount from 'private_key' where keyname='%s';", keyuuid);
   return times;
}

void KeyDB::MarkKeyPairAsExpendable(const char *keyuuid)
{
   sqlite3_mutex_enter(dbmutex);
   SQLInstantExecf_unsafe("update 'private_key' set latest='E' where keyname='%s';", keyuuid);
   SQLInstantExecf_unsafe("update 'private_key' set usedrefcount='0' where keyname='%s';", keyuuid);
   SQLInstantExecf_unsafe("update 'private_key' set existsrefcount='0' where keyname='%s';", keyuuid);
   SQLInstantExecf_unsafe("update 'public_key' set latest='E' where keyname='%s';", keyuuid);
   SQLInstantExecf_unsafe("update 'public_key' set usedrefcount='0' where keyname='%s';", keyuuid);
   SQLInstantExecf_unsafe("update 'public_key' set existsrefcount='0' where keyname='%s';", keyuuid);
   sqlite3_mutex_leave(dbmutex);
}

void KeyDB::MarkKeyPairAsLatest(const char *keyuuid)
{
   sqlite3_mutex_enter(dbmutex);
   SQLInstantExecf_unsafe("update 'private_key' set latest='Y' where keyname='%s';", keyuuid);
   SQLInstantExecf_unsafe("update 'public_key' set latest='Y' where keyname='%s';", keyuuid);
   sqlite3_mutex_leave(dbmutex);
}

void KeyDB::MarkKeyPairAsOld(const char *keyuuid)
{
   sqlite3_mutex_enter(dbmutex);
   SQLInstantExecf_unsafe("update 'private_key' set latest='N' where keyname='%s';", keyuuid);
   SQLInstantExecf_unsafe("update 'public_key' set latest='N' where keyname='%s';", keyuuid);
   sqlite3_mutex_leave(dbmutex);
}

void KeyDB::MarkKeyPairAsLatestButExpendableWhenZero(const char *keyuuid)
{
   sqlite3_mutex_enter(dbmutex);
   SQLInstantExecf_unsafe("update 'private_key' set latest='E' where keyname='%s';", keyuuid);
   SQLInstantExecf_unsafe("update 'public_key' set latest='E' where keyname='%s';", keyuuid);
   sqlite3_mutex_leave(dbmutex);
}

std::vector<char *> *KeyDB::GetPrivateKeyNamesWithUsedCountAtZeroAndOwner(const char *owner)
{
   std::vector<char *> *output = SQLScanAllTextf("select keyname from 'private_key' where owner='%s' and usedrefcount='0';", owner);
   return output;
}

void KeyDB::ReplaceMainKeyPairWithUpdatedVersions(const char *owner)
{
   char *latestkeyname = GetLatestPublicKeyNameOfOwner(owner);
   if (latestkeyname == NULL)
   {
      errprint("could not get latest key with owner '%s'", owner);
      return;
   }
   KeyPair *prv_kp = GetPrivateKeyByName(latestkeyname);
   if (prv_kp == NULL)
   {
      errprint("could not get the latest private key that is owned by '%s'", owner);
      return;
   }
   KeyPair *pub_kp = GetPublicKeyByName(latestkeyname);
   if (pub_kp == NULL)
   {
      errprint("could not get the latest public key that is owned by '%s'", owner);
      return;
   }
   RSAKeyPair *newrsa = new RSAKeyPair();
   newrsa->RSAMergeKeyPair((RSAKeyPair *)pub_kp, (RSAKeyPair *)prv_kp);
   delete pub_kp;
   delete prv_kp;
   // only the self key pair is a true key pair, the other have blank keys inserted to
   // ensure nothing is leaked or broken
   const char *selfkeycopy = selfkey;
   KeyPair *oldkeypair = selfkeypair;
   selfkeypair = (KeyPair *)newrsa;
   selfkey = latestkeyname;
   free((void *)selfkeycopy);
   delete oldkeypair;
}

void KeyDB::PromoteKeyOfUnauthorised(const char *name)
{
   SQLInstantExecf("update 'public_key' set keyname='%s',owner='%s' where keyname='.%s';", name, name, name);
   SQLInstantExecf("update 'private_key' set keyname='%s',owner='%s' where keyname='.%s';", name, name, name);
}

int KeyDB::CheckForPublicKeyByName(const char *name)
{
   std::vector<char *> *dep_names =
      SQLScanRowTextf("select keyname from 'public_key' where keyname='%s';", name);
   int retval = ((dep_names) && (dep_names->size() > 0));
   if (dep_names) StringUtils::DestroyStringList(dep_names);
   return retval;
}

int KeyDB::IsKeyIntendedForCompute(const char *name)
{
   int retval = 0;
   std::vector<char *> *dep_names =
      SQLScanRowTextf("select owner from 'public_key' where keyname='%s';", name);
   if ((dep_names) && (dep_names->size() > 0))
   {
      char *newstr = (char *)malloc(sizeof(char)*(strlen(dep_names->at(0))+1));
      strcpy(newstr, dep_names->at(0));
      char *remainder = NULL;
      char *ttoken = apr_strtok(newstr, "_", &remainder);
      retval = (strcmp(ttoken, ".compute") == 0);
      free(newstr);
   }
   if (dep_names) StringUtils::DestroyStringList(dep_names);
   return retval;
}

char *KeyDB::GetWorkStoreFromComputeKey(const char *name)
{
   int retval = 0;
   std::vector<char *> *dep_names =
      SQLScanRowTextf("select owner from 'public_key' where keyname='%s';", name);
   if ((dep_names) && (dep_names->size() > 0))
   {
      char *newstr = (char *)malloc(sizeof(char)*(strlen(dep_names->at(0))+1));
      strcpy(newstr, dep_names->at(0));
      char *remainder = NULL;
      char *ttoken = apr_strtok(newstr, "_", &remainder);
      ttoken = apr_strtok(NULL, "_", &remainder);
      char *newstr2 = NULL;
      if (ttoken != NULL)
      {
         newstr2 = (char *)malloc(sizeof(char)*(strlen(ttoken)+1));
         strcpy(newstr2, ttoken);
      }
      free(newstr);
      if (dep_names) StringUtils::DestroyStringList(dep_names);
      return newstr2;
   }
   if (dep_names) StringUtils::DestroyStringList(dep_names);
   return NULL;
}

void KeyDB::ShowKeyStatus()
{
   std::vector<char *> *all_pkeynames = NULL;
   all_pkeynames = SQLScanAllTextf("select keyname,owner,latest,usedrefcount,existsrefcount from 'private_key';");
   if ((all_pkeynames == NULL) || (all_pkeynames->size() == 0))
   {
      errprint("there are no private keys?");
      if (all_pkeynames) delete all_pkeynames;
   } else
   {
      for (int i = 0; i < (all_pkeynames->size() / 5); i++)
      {
         std::vector<char *> *this_pubkey =
            SQLScanRowTextf("select keyname,owner,latest,usedrefcount,existsrefcount from 'public_key' where keyname='%s';", all_pkeynames->at(i*5+0));
         printf("KEY %s:\n", all_pkeynames->at(i*5+0));
         if ((this_pubkey == NULL) || (this_pubkey->size() == 0))
         {
            if (this_pubkey) delete this_pubkey;
         } else
         {
            printf("PUB %s %c(u%d,e%d)\n", this_pubkey->at(1),
               (this_pubkey->at(2))[0], atoi(this_pubkey->at(3)), atoi(this_pubkey->at(4)));
            StringUtils::DestroyStringList(this_pubkey);
         }
         printf("PRV %s %c(u%d,e%d)\n\n", all_pkeynames->at(i*5+1),
            (all_pkeynames->at(i*5+2))[0], atoi(all_pkeynames->at(i*5+3)), atoi(all_pkeynames->at(i*5+4)));
      }
      StringUtils::DestroyStringList(all_pkeynames);
   }

   all_pkeynames = SQLScanAllTextf("select keyname,owner,latest,usedrefcount,existsrefcount from 'public_key';");
   if ((all_pkeynames == NULL) || (all_pkeynames->size() == 0))
   {
      errprint("there are no public keys?");
      if (all_pkeynames) delete all_pkeynames;
   } else
   {
      for (int i = 0; i < (all_pkeynames->size() / 5); i++)
      {
         std::vector<char *> *this_prvkey =
            SQLScanRowTextf("select keyname,owner,latest,usedrefcount,existsrefcount from 'private_key' where keyname='%s';", all_pkeynames->at(i*5+0));
         if ((this_prvkey == NULL) || (this_prvkey->size() == 0))
         {
            if (this_prvkey) delete this_prvkey;
            printf("KEY %s:\n", all_pkeynames->at(i*5+0));
            printf("PUB %s %c(u%d,e%d)\n\n", all_pkeynames->at(i*5+1),
               (all_pkeynames->at(i*5+2))[0], atoi(all_pkeynames->at(i*5+3)), atoi(all_pkeynames->at(i*5+4)));
         } else
         {
            StringUtils::DestroyStringList(this_prvkey);
         }
      }
      StringUtils::DestroyStringList(all_pkeynames);
   }
}

void KeyDB::CleanOldKeys()
{
   //ShowKeyStatus();
   sqlite3_mutex_enter(dbmutex);
   // Need to check for key removal, and remove user if one of these keys is a main user key
   SQLInstantExecf_unsafe("delete from 'public_key' where latest='D' and existsrefcount='0';");
   SQLInstantExecf_unsafe("delete from 'private_key' where latest='D' and existsrefcount='0';");
   SQLInstantExecf_unsafe("update 'public_key' set latest='D' where latest='N' and existsrefcount='0';");
   SQLInstantExecf_unsafe("update 'private_key' set latest='D' where latest='N' and existsrefcount='0';");
   SQLInstantExecf_unsafe("delete from 'public_key' where latest='X' and existsrefcount='0';");
   SQLInstantExecf_unsafe("delete from 'private_key' where latest='X' and existsrefcount='0';");
   SQLInstantExecf_unsafe("update 'public_key' set latest='X' where latest='E' and existsrefcount='0';");
   SQLInstantExecf_unsafe("update 'private_key' set latest='X' where latest='E' and existsrefcount='0';");
   sqlite3_mutex_leave(dbmutex);
}
